package leilei.jace.com.tcpdemo2.service;

import java.net.InetSocketAddress;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;
import io.netty.handler.timeout.IdleStateHandler;
import leilei.jace.com.tcpdemo2.text.ConstantValue;
import leilei.jace.com.tcpdemo2.util.LogUtil;
import leilei.jace.com.tcpdemo2.util.SmartCarDecoder;
import leilei.jace.com.tcpdemo2.util.SmartCarEncoder;

/**
 * Created by leilei on 2018/4/8.
 */

public class TcpService {
    private ExecutorService executorService = Executors.newFixedThreadPool(20);

    private TcpService() {
    }

    private static class SingletonHolder {
        private static final TcpService INSTANCE = new TcpService();
    }

    public static TcpService getInstance() {
        return SingletonHolder.INSTANCE;
    }

    public synchronized void StartTcpService() {
        executorService.execute(new Runnable() {
            @Override
            public void run() {
                TcpService.getInstance().StartService();
            }
        });
    }

    public void StartService() {
        // Boss线程：由这个线程池提供的线程是boss种类的，用于创建、连接、绑定socket， （有点像门卫）然后把这些socket传给worker线程池。
        // 在服务器端每个监听的socket都有一个boss线程来处理。在客户端，只有一个boss线程来处理所有的socket。
        EventLoopGroup bossGroup = new NioEventLoopGroup();
        // Worker线程：Worker线程执行所有的异步I/O，即处理操作
        EventLoopGroup workerGroup = new NioEventLoopGroup();
        try {
            // ServerBootstrap 启动NIO服务的辅助启动类,负责初始话netty服务器，并且开始监听端口的socket请求
            ServerBootstrap b = new ServerBootstrap();
            b.group(bossGroup, workerGroup)
                    .channel(NioServerSocketChannel.class)// 设置非阻塞,用它来建立新accept的连接,用于构造serversocketchannel的工厂类
                    .handler(new LoggingHandler(LogLevel.INFO))
                    .localAddress(new InetSocketAddress(ConstantValue.TCP_PORT))
                    .childHandler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(io.netty.channel.socket.SocketChannel ch) throws Exception {
                            // 设置5秒没有读到数据，则触发一个READER_IDLE事件。
                            ch.pipeline().addLast(new IdleStateHandler(5, 0, 0, TimeUnit.SECONDS));
                            ch.pipeline().addLast(new SmartCarEncoder());
                            ch.pipeline().addLast(new SmartCarDecoder());
                            ch.pipeline().addLast(new ServerHandler());
                        }
                    });
            ChannelFuture f = b.bind().sync();
            if (f.isSuccess()) {
                LogUtil.e("服务器连接客户端启动成功", "服务器连接客户端启动成功");
            } else {
                LogUtil.e("服务器连接客户端启动失败", "服务器连接客户端启动失败");
            }
            f.channel().closeFuture().sync();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            bossGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
        }
    }
}
